-- Copyright (c) 2024 Huawei Technologies Co., Ltd.
-- openUBMC is licensed under Mulan PSL v2.
-- You can use this software according to the terms and conditions of the Mulan PSL v2.
-- You may obtain a copy of Mulan PSL v2 at:
--         http://license.coscl.org.cn/MulanPSL2
-- THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
-- EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
-- MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
-- See the Mulan PSL v2 for more details.

local class = require "mc.class"
local abstract_chk = require "util.check.abstract_check"
local prop_def = require 'macros.property_def'
local log = require 'mc.logging'

local config_check = {}

-- String类型检查
local TStringCheck = class(abstract_chk)

function TStringCheck.check_type(attribute, value)
    if type(value) ~= prop_def.STRING_TYPE then
        log:error("TStringCheck:check_type type invalid.")
        return prop_def.REGRIST_TYPE_PROP_TYPE_ERROR
    end

    local max_len = 0
    local min_len = 0

    local len = attribute:get_val_by_name(prop_def.REGRIST_PROP_MAXLENGTH)
    if len ~= nil then
        max_len = len
    end

    len = attribute:get_val_by_name(prop_def.REGRIST_PROP_MINLENGTH)
    if len ~= nil then
        min_len = len
    end

    local input_len = #value
    if (max_len ~= 0) and (input_len < min_len or input_len > max_len) then
        log:error("TStringCheck.check_type len(%d) invalid, min_len(%s) max_len(%s)", input_len, min_len, max_len)
        return prop_def.REGRIST_TYPE_PROP_LENGTH_ERR
    end

    -- 获取正则表达式
    local attr_expression = attribute:get_val_by_name(prop_def.REGRIST_PROP_VALUEEXPRESSION)
    if type(attr_expression) == 'nil' then
        log:error("TStringCheck.check_type attr_expression is nil")
        return prop_def.E_OK
    elseif type(attr_expression) == prop_def.STRING_TYPE then
        return prop_def.E_OK
    else
        return prop_def.E_FAILURE
    end
end

config_check.StringCheck = TStringCheck

-- Enum类型检查
local TEnumCheck = class(abstract_chk)

function TEnumCheck.check_type(attribute, value)
    if type(value) ~= prop_def.STRING_TYPE then
        log:error("TEnumCheck:check_type type invalid.")
        return prop_def.REGRIST_TYPE_PROP_TYPE_ERROR
    end

    -- 获取数组
    local enum_list = attribute:get_enum_list()
    if enum_list == nil then
        log:error("TEnumCheck:check_type get enum_list fail.")
        return prop_def.E_FAILURE
    end

    local len = #enum_list
    for i = 1, len, 1 do
        local enum = enum_list[i]
        if enum == nil then
            log:error("TEnumCheck:check_type get enum_list[%d] fail.", i)
            return prop_def.E_FAILURE
        end

        local prop_name = enum:get_val_by_name(prop_def.REGRIST_PROP_VALUENAME)
        if prop_name == nil then
            log:error("TEnumCheck:check_type get ValueName fail.", i)
            return prop_def.E_FAILURE
        end

        -- 配置在列表中
        if prop_name == value then
            return prop_def.E_OK
        end
    end

    log:debug("TEnumCheck:check_type not find")
    return prop_def.REGRIST_TYPE_PROP_NOT_LIST
end

config_check.EnumCheck = TEnumCheck

-- Integer类型检查
local TIntegerCheck = class(abstract_chk)

local function check_increment(attribute, valid, value)
    local scalarincrement = 0
    local currentvalue = 0
    if valid then
        local scalar_increment = attribute:get_val_by_name(prop_def.REGRIST_PROP_SCALARINCREMET)
        local current_value = attribute:get_val_by_name(prop_def.REGRIST_PROP_CURRENTVALUE)
        if type(scalar_increment) ~= prop_def.NUMBER_TYPE or type(current_value) ~= prop_def.NUMBER_TYPE then
            return prop_def.E_FAILURE
        end

        if scalar_increment ~= nil then
            scalarincrement = scalar_increment
        end

        if current_value ~= nil then
            currentvalue = current_value
        end

        if scalarincrement == 0 then
            return prop_def.E_OK
        else
            local double_value = (value - currentvalue) % (scalarincrement)
            if double_value == 0 then
                return prop_def.E_OK
            else
                return prop_def.REGRIST_TYPE_PROP_SCALAR_INCREMENT
            end
        end
    end

    return prop_def.E_FAILURE
end

function TIntegerCheck.check_type(attribute, value)
    if type(value) ~= prop_def.NUMBER_TYPE then
        log:error("TIntegerCheck:check_type type invalid.")
        return prop_def.REGRIST_TYPE_PROP_TYPE_ERROR
    end

    local max_number = 0
    local min_number = 0

    local upper_bound = attribute:get_val_by_name(prop_def.REGRIST_PROP_UPPERBOUND)
    local lower_bound = attribute:get_val_by_name(prop_def.REGRIST_PROP_LOWERBOUND)
    if type(upper_bound) ~= prop_def.NUMBER_TYPE or type(lower_bound) ~= prop_def.NUMBER_TYPE then
        log:error("TIntegerCheck:check_type upper_bound type invalid.")
        return prop_def.E_FAILURE
    end

    if upper_bound ~= nil then
        max_number = upper_bound
    end

    if lower_bound ~= nil then
        min_number = lower_bound
    end

    if (max_number ~= 0) and (value < min_number or value > max_number) then
        log:info("TIntegerCheck.check_type len(%d) invalid, min_len(%s) max_len(%s)", value, min_number, max_number)
        return prop_def.REGRIST_TYPE_PROP_NOT_RANGE
    end

    -- 获取正则表达式
    local attr_expression = attribute:get_val_by_name(prop_def.REGRIST_PROP_VALUEEXPRESSION)
    local valid = false
    if type(attr_expression) == 'nil' or type(attr_expression) == 'userdata' then
        log:info("TIntegerCheck.check_type attr_expression is nil")
        valid = true
    elseif type(attr_expression) == prop_def.STRING_TYPE then
        valid = true
    else
        valid = false
    end

    return check_increment(attribute, valid, value)
end

config_check.IntegerCheck = TIntegerCheck

-- Boolean类型检查
local TBooleanCheck = class(abstract_chk)

function TBooleanCheck.check_type(attribute, value)
    if type(value) ~= prop_def.BOOLEAN_TYPE then
        log:error("TBooleanCheck:check_type type invalid.")
        return prop_def.REGRIST_TYPE_PROP_TYPE_ERROR
    end

    return prop_def.E_OK
end

config_check.BooleanCheck = TBooleanCheck

-- Password类型检查
local TPasswordCheck = class(abstract_chk)

function TPasswordCheck.check_type(attribute, value)
    if type(value) ~= prop_def.STRING_TYPE then
        log:error("TPasswordCheck:check_type type invalid.")
        return prop_def.REGRIST_TYPE_PROP_TYPE_ERROR
    end

    return prop_def.E_OK
end

config_check.PasswordCheck = TPasswordCheck

return config_check